package org.prowl.torquescan;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import monitor.model.parameter.EcuParameter;
import monitor.model.parameter.EcuParameterType;
import monitor.model.parameter.TimePoint;

import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;

import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;


public class ParameterRepo {
	//TimePoint is made every minute
	private final static long TIME_POINT_INTERVAL_MILLIS = 1000 * 60;//one minute
	
	//Snapshot send interval(To cloud data is send at periodical interval of time)
	private final static long SNAPSHOT_INTERVAL_MILLIS = TIME_POINT_INTERVAL_MILLIS * 5;
	//Location of REST service in the cloud app
	private final static String PATH = "http://car-monitoring.appspot.com/rest/android/history-update/";
	private static String carId;
	
	private static Map<EcuParameterType, ArrayList<Float>> tempParams = new HashMap<EcuParameterType, ArrayList<Float>>();	
	private static ArrayList<TimePoint> timePoints = new ArrayList<TimePoint>();
	private static Timer timePointTimer;
	private static Timer snapshotTimer;
	private static boolean stopped = true;
	private static LocationManager locationManager;
	private static String locationProvider = LocationManager.NETWORK_PROVIDER;
	private static Location location;

	// Define a listener that responds to location updates
	private static LocationListener locationListener = new LocationListener() {
	    public void onLocationChanged(Location location) {
	      // Called when a new location is found by the network location provider.
	      ParameterRepo.location = location;
	    }

	    public void onStatusChanged(String provider, int status, Bundle extras) {}

	    public void onProviderEnabled(String provider) {}

	    public void onProviderDisabled(String provider) {}

	};
	  
	private ParameterRepo() {
	}
	
	//Stop sending of data to cloud
	public static void stop() {
		stopped = true;
		if(timePointTimer != null) {
			timePointTimer.cancel();
		}
		if(snapshotTimer != null) {
			snapshotTimer.cancel();
		}
	}
	
	//start sending of data to cloud
	public static void start() {
		if(stopped) {
			stopped = false;
			
			timePointTimer = new Timer();
			timePointTimer.schedule(new TimerTask() {
				public void run() {
					createTimePoint();
				}
			}, TIME_POINT_INTERVAL_MILLIS, TIME_POINT_INTERVAL_MILLIS);
			
			snapshotTimer = new Timer();
			snapshotTimer.schedule(new TimerTask() {
				public void run() {
					sendSnapshot();
				}
			}, SNAPSHOT_INTERVAL_MILLIS, SNAPSHOT_INTERVAL_MILLIS);
		}
	}
	
	public static void addParameter(EcuParameterType t, float value) {
		if(stopped) {
			return;
		}
		
		ArrayList<Float> list = tempParams.get(t);
		if(list == null) {
			list = new ArrayList<Float>();
			tempParams.put(t, list);
		}
		list.add(value);
	}
	
	/**
	 * ECU data from car is obtained very often(200ms)
	 * This method creates TimePoint object, which stores AVG and MAX values of them.
	 * Adds location and creation date to TimePoint object
	 */
	private static void createTimePoint() {
		ArrayList<EcuParameter> ecuParameters = new ArrayList<EcuParameter>();
		Set<EcuParameterType> paramNames = tempParams.keySet();
		Iterator<EcuParameterType> iter = paramNames.iterator();
		
		while(iter.hasNext()) {
			EcuParameterType paramType = iter.next();
			ArrayList<Float> paramList = tempParams.get(paramType);
			if(paramList == null) {
				continue;
			}
			ArrayList<Float> paramListCloned = (ArrayList<Float>)paramList.clone();
			paramList.clear();
			
			float sum = 0.0f;
			float valueMax = 0.0f;
			
			for (float value : paramListCloned) {
				sum += value;
				if(value > valueMax) {
					valueMax = value;
				}
			}
			
			float valueAvg = 0.0f;
			if(paramListCloned.size() > 0){
				valueAvg = sum / paramListCloned.size();
			}
			ecuParameters.add(new EcuParameter(paramType, valueAvg, valueMax));			
		}
		
		TimePoint timePoint = new TimePoint();
		timePoint.setParameters(ecuParameters);
		timePoint.setDateMillis(new Date().getTime());
		if(location != null) {
			timePoint.setLat(location.getLatitude());
			timePoint.setLng(location.getLongitude());
		}
		
		timePoints.add(timePoint);
	}
	
	/**
	 * Send collected data to cloud
	 */
	private static void sendSnapshot() {
		ArrayList<TimePoint> timePointsCloned = (ArrayList<TimePoint>)timePoints.clone();
		if(timePointsCloned == null || timePointsCloned.size() == 0) {
			return;
		}
		timePoints.clear();
		
		StringBuilder json = new StringBuilder();
		json.append("[");
		for (int i = 0; i < timePointsCloned.size() - 1; i++) {
			json.append(timePointsCloned.get(i).toString() + ",");
		}
		TimePoint lastTimePoint = timePointsCloned.get(timePointsCloned.size() - 1);
		json.append(lastTimePoint.toString());
		json.append("]");
		
		try {
			DefaultHttpClient httpclient = new DefaultHttpClient();
			
			HttpPost httpost = new HttpPost(PATH + carId);
			String httpBodyTEST = json.toString();
			StringEntity se = new StringEntity(httpBodyTEST);
			httpost.setEntity(se);
			httpost.setHeader("Accept", "application/json");
			httpost.setHeader("Content-type", "application/json");

			ResponseHandler responseHandler = new BasicResponseHandler();
			httpclient.execute(httpost, responseHandler);
		} catch (Exception e) {
			Log.e(null, e.getMessage(), e);
		}
		Log.i(null, "sendSnapshotEnd");
	}

	public static String getCarId() {
		return carId;
	}

	public static void setCarId(String carId) {
		ParameterRepo.carId = carId;
	}

	public static LocationManager getLocationManager() {
		return locationManager;
	}

	public static void setLocationManager(LocationManager locationManager) {
		ParameterRepo.locationManager = locationManager;
		locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
	}
}
